#include <Core/Box/BoxAsioTransportImpl.h>

#define BOX_ASIO_TRANSPORT_BAD_WAITING 1

#define STD_EXCEPT_SAFE_BEGIN try {
#define STD_EXCEPT_SAFE_END } catch (std::exception const & expt) { }
#define STD_EXCEPT_SAFE_END_RETHROW } catch (std::exception const & expt) { throw TransportLayerException(expt.what()); }

namespace BoxCore {

/**
 * Public BoxAsioTransport's api
 */
BoxAsioTransport::BoxAsioTransport(BoxAsioTransportParams const & params) : 
	m_params(params),
 	m_sockIOService(),
	m_sockAcceptor(m_sockIOService, boost::asio::ip::tcp::endpoint(
		boost::asio::ip::tcp::v4(), params.port)),
	m_sockIOServiceLoop(),
	m_commandIface()
{ 

}

BoxAsioTransport::~BoxAsioTransport() throw () 
{ 
	interrupt(true);
}

void BoxAsioTransport::performAsync(IBoxCommandIFacePtr commandIFace) throw (TransportLayerException) 
{
	STD_EXCEPT_SAFE_BEGIN

	m_commandIface = commandIFace;
	
	if (m_sockIOServiceLoop)
		throw (TransportLayerException("BoxAsioTransport all ready works."));

	Aux::BoxAsioTransportSessionPtr newSession(new Aux::BoxAsioTransportSession(m_commandIface, m_sockIOService));
	m_sockAcceptor.async_accept(
		newSession->getSocket(), 
		boost::bind(&BoxAsioTransport::waitNextAccept, 
					this,
					newSession,
					boost::asio::placeholders::error));

	m_sockIOServiceLoop.reset(new boost::thread(boost::bind(&BoxAsioTransport::asioIOServiceLoop, this)));
	
	STD_EXCEPT_SAFE_END_RETHROW
} 

void BoxAsioTransport::interrupt(bool closeGraceful) throw (TransportLayerException) 
{
	STD_EXCEPT_SAFE_BEGIN

	m_commandIface->interrupt(closeGraceful);
	m_sockIOService.stop();

	STD_EXCEPT_SAFE_END_RETHROW
}

void BoxAsioTransport::wait() throw () 
{
	if (m_sockIOServiceLoop) 
#if defined(BOX_ASIO_TRANSPORT_BAD_WAITING)
#	warning Bad waiting implmentation os on!
		m_sockIOServiceLoop->join();
#else
	// TODO impl this
#endif
}

/**
 * Private BoxAsioTransport's api
 */

void BoxAsioTransport::asioIOServiceLoop() 
{
	for (;;) {
		STD_EXCEPT_SAFE_BEGIN
		m_sockIOService.run();
		break;
		STD_EXCEPT_SAFE_END
	} // asio loop
}

void BoxAsioTransport::waitNextAccept(
	Aux::BoxAsioTransportSessionPtr prevSession , boost::system::error_code const & error) 
{
	startNewSessionAsync(prevSession);
	Aux::BoxAsioTransportSessionPtr newSession(new Aux::BoxAsioTransportSession(m_commandIface, m_sockIOService));
	m_sockAcceptor.async_accept(
		newSession->getSocket(), 
		boost::bind(&BoxAsioTransport::waitNextAccept, 
					this,
					newSession,
					boost::asio::placeholders::error));
}

} // namespace BoxCore

#undef STD_EXCEPT_SAFE_BEGIN
#undef STD_EXCEPT_SAFE_END
#undef STD_EXCEPT_SAFE_END_RETHROW

